/*
 * topology.c
 *
 *  Created on: Sep 6, 2013
 *      Author: cinus
 */

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>

#include "topology.h"
#include "activation.h"
#include "errorfunctions.h"

#define MAX_LINE_LENGTH 1024
#define COMMENT_CHAR '#'
#define MAX(a,b) \
   ({ __typeof__ (a) _a = (a); \
       __typeof__ (b) _b = (b); \
     _a > _b ? _a : _b; })

void readNumberofLayers(topology_t* tp, FILE *fp);
uint16 readNeuronsPerLayer(topology_t* tp, FILE *fp);
uint8 readErrorFunction(topology_t* tp, FILE *fp, int8* filename);
uint8 readActivationFunctions(topology_t* tp, FILE *fp, int8* filename, uint16 numNeurons);
uint8 readConnections(topology_t* tp, FILE *fp, int8* filename, uint16 numNeurons);

void setFullyConnectedLayers(topology_t*tp, uint16 numNeurons);

uint8 readTopology(topology_t* tp, int8* filename) {
	FILE *fp;
	//int8 line[MAX_LINE_LENGTH];
	//int8 npl[MAX_LINE_LENGTH];
	//int8 *token;

	//uint16 i = 0, j = 0, numNeurons = 0, len = 0;
	uint16 numNeurons = 0;
	uint8 status;

	fp = fopen(filename, "r");
	if (fp == NULL) {
		fprintf(stderr, "File %s not found\n", filename);
		return EXIT_FAILURE;
	}

	readNumberofLayers(tp, fp);
	numNeurons = readNeuronsPerLayer(tp, fp);
	//detecting the error fucntion
	status = readErrorFunction(tp, fp, filename);
	if (status != EXIT_SUCCESS) {
		fclose(fp);
		fprintf(stderr, "FAILURE\n");
		return status;
	}
	status = readActivationFunctions(tp, fp, filename, numNeurons);
	if (status != EXIT_SUCCESS) {
		fclose(fp);
		return status;
	}
	status = readConnections(tp, fp, filename, numNeurons);
	if (status == 2) { //no connections section found, assuming fully connected x layer
		setFullyConnectedLayers(tp, numNeurons);
	} else if (status == EXIT_FAILURE) {
		fclose(fp);
		return status;
	}
	fclose(fp);
	return EXIT_SUCCESS;
}

void readNumberofLayers(topology_t* tp, FILE *fp) {
	int8 line[MAX_LINE_LENGTH];
	//read the number of layers
	do {
		fgets(line, MAX_LINE_LENGTH, fp);
		//assert(fp != NULL);
	} while (line[0] == COMMENT_CHAR);

	sscanf(line, "%*s %hd", &(tp->layers));
//fprintf(stderr,"%s\n",line);
//fprintf(stderr, "Layers: %d\n", tp->layers);
}

uint16 readNeuronsPerLayer(topology_t* tp, FILE *fp) {
	int8 line[MAX_LINE_LENGTH];
	int8 *token;
	uint16 i = 0, numNeurons = 0;
//read the neurons x layer
	do {
		fgets(line, MAX_LINE_LENGTH, fp);
//assert(fp != NULL);
	} while (line[0] == COMMENT_CHAR);
	tp->neurons = (uint32*) malloc(tp->layers * sizeof(uint32));
	token = strtok(line, ": ");
	while (token != NULL ) {
//fprintf(stderr, "%s\n", token);
		if (i != 0) {
			//fprintf(stderr,"%d -> %s\n",i, token);
			tp->neurons[i - 1] = atoi(token);
			numNeurons += tp->neurons[i - 1];
		}
		i++;
		token = strtok(NULL, ": ");
	}
	return numNeurons;
}

uint8 readErrorFunction(topology_t* tp, FILE *fp, int8* filename) {
	int8 line[MAX_LINE_LENGTH];
	int8 errorFunctionString[MAX_LINE_LENGTH];
	do {
		fgets(line, MAX_LINE_LENGTH, fp);
		//assert(fp != NULL);
	} while (line[0] == COMMENT_CHAR);
	sscanf(line, "%*s %s\n", &errorFunctionString);
	/*fprintf(stderr, "%s", errorFunctionString);
	 fprintf(stderr, "\n", errorFunctionString);*/
	if (strcmp(errorFunctionString, "CROSS_ENTROPY")) {
		tp->errorFunction = crossEntropy;
	} else if (strcmp(errorFunctionString, "SUM_OF_SQUARES")) {
		tp->errorFunction = sumOfSquares;
	} else if (strcmp(errorFunctionString, "MEAN_SQUARE_ERROR")) {
		tp->errorFunction = meanSquaredError;
	} else {
		fprintf(stderr, "Wrong file format in %s, expected ERROR: [CROSS_ENTROPY | SUM_OF_SQUARES | MEAN_SQUARE_ERROR]: but found %s \n", filename, line);
		return EXIT_FAILURE;
	}
	return EXIT_SUCCESS;
}
uint8 readActivationFunctions(topology_t* tp, FILE *fp, int8* filename, uint16 numNeurons) {
	int8 line[MAX_LINE_LENGTH];
	int8 *token;

	uint16 len, i, m, n;

//read the activation functions
	do {
		fgets(line, MAX_LINE_LENGTH, fp);
		//assert(fp != NULL);
	} while (line[0] == COMMENT_CHAR);
//fgets(line, MAX_LINE_LENGTH, fp);
	len = strlen(line);
	//assert(fp != NULL);
	line[len - 1] = '\0';
	if (strcmp(line, "ACTIVATIONS_FUNCTIONS:") != 0) {
		fprintf(stderr, "Wrong file format in %s, expected ACTIVATIONS_FUNCTIONS: but found %s \n", filename, line);
		return EXIT_FAILURE;
	}

//dynamically allocate an array of function pointers returning a float and having a netdata_tp and a float as parameter
	tp->activationFunctions = (float32 (**)(netdata_tp, float32)) calloc(numNeurons, sizeof(float32 (*)(netdata_tp, float32)));
	tp->diffActivationFunctions = (float32 (**)(netdata_tp, float32)) calloc(numNeurons, sizeof(float32 (*)(netdata_tp, float32)));
	tp->indexes = (index_t*) calloc(numNeurons, sizeof(index_t));
	//assert(tp->indexes != NULL);
	for (i = 0; i < numNeurons; i++) {
		do {
			fgets(line, MAX_LINE_LENGTH, fp);
			//assert(fp != NULL);
		} while (line[0] == COMMENT_CHAR);

		token = strtok(line, "n(,) ");
		m = atoi(token);
		token = strtok(NULL, "n(,) \n");
		n = atoi(token);
		tp->indexes[i].layer = m;
		tp->indexes[i].pos = n;
//fprintf(stderr, "tp->indexes[%d] = {%d,%d}\n", i, tp->indexes[i].layer, tp->indexes[i].pos);
		token = strtok(NULL, "n(,) \n");

		if (strcmp(token, "INPUT") == 0) {

		} else if (strcmp(token, "SIGMOID") == 0) {
			tp->activationFunctions[i] = activationSigmoid;
			tp->diffActivationFunctions[i] = diffActivationSigmoid;
		} else if (strcmp(token, "TANH") == 0) {
			tp->activationFunctions[i] = activationTanh;
			tp->diffActivationFunctions[i] = diffActivationTanh;
		} else if (strcmp(token, "LINEAR") == 0) {
			tp->activationFunctions[i] = activationLinear;
			tp->diffActivationFunctions[i] = diffActivationLinear;
		} else if (strcmp(token, "SOFTMAX") == 0) {
			tp->activationFunctions[i] = activationSoftmax;
			tp->diffActivationFunctions[i] = diffActivationSoftmax;
		} else {
			fprintf(stderr, "Wrong file format in %s, expected [INPUT | SIGMOID | TANH | LINEAR] but found %s \n", filename, token);
			return EXIT_FAILURE;
		}
	}
	do {
		fgets(line, MAX_LINE_LENGTH, fp);
//assert(fp != NULL);
	} while (line[0] == COMMENT_CHAR);
	len = strlen(line);
//assert(fp != NULL);
	line[len - 1] = '\0';
	if (strcmp(line, "END_ACTIVATIONS_FUNCTIONS") != 0) {
		fprintf(stderr, "Wrong file format in %s, expected END_ACTIVATIONS_FUNCTIONS but found %s \n", filename, line);
		return EXIT_FAILURE;
	}
	return EXIT_SUCCESS;
}

uint8 readConnections(topology_t* tp, FILE *fp, int8* filename, uint16 numNeurons) {
	int8 line[MAX_LINE_LENGTH];
	int8 *token;

	uint32 len, i, j;
//Reading the connections
	do {
		fgets(line, MAX_LINE_LENGTH, fp);
		//assert(fp != NULL);
	} while (line[0] == COMMENT_CHAR);
	len = strlen(line);
//assert(fp != NULL);
	line[len - 1] = '\0';
	if (strcmp(line, "CONNECTIONS:") != 0) {
		//fprintf(stderr, "Wrong file format in %s, expected CONNECTIONS: but found %s \n", filename, line);
		return 2;
	}

	tp->connections = (uint8**) malloc(numNeurons * sizeof(uint8*));
//fprintf(stderr, "# neurons --> %d\n", numNeurons);
	for (i = 0; i < numNeurons; i++) {
		do {
			fgets(line, MAX_LINE_LENGTH, fp);
			//assert(fp != NULL);
		} while (line[0] == COMMENT_CHAR);
		len = strlen(line);
		//assert(fp != NULL);
		line[len - 1] = '\0';
		j = 0;
		tp->connections[i] = (uint32*) malloc(numNeurons * sizeof(uint32));
		token = strtok(line, ": \t");
		while (token != NULL ) {
			//fprintf(stderr,"%s ", token);
			tp->connections[i][j] = atoi(token);
			token = strtok(NULL, ": \t");
			j++;
		}
//fprintf(stderr,"\n");

	}

	do {
		fgets(line, MAX_LINE_LENGTH, fp);
//assert(fp != NULL);
	} while (line[0] == COMMENT_CHAR);
	len = strlen(line);
//assert(fp != NULL);
	line[len - 1] = '\0';
	if (strcmp(line, "END_CONNECTIONS") != 0) {
		fprintf(stderr, "Wrong file format in %s, expected END_CONNECTIONS but found %s \n", filename, line);
		return EXIT_FAILURE;
	}
	return EXIT_SUCCESS;
}

uint32 sumArray(uint16* array, uint32 index) {
	uint32 i, sum = 0;

	for (i = 0; i < index; i++) {
		sum += array[i];
	}
	return sum;
}
void setFullyConnectedLayers(topology_t* tp, uint16 numNeurons) {
	fprintf(stderr, "Not yet implemented\n");
	exit(EXIT_FAILURE);
}

void freeTopology(topology_t* tp) {
	uint32 i;
	uint32 numNeurons;

	numNeurons = 0;
	for (i = 0; i < tp->layers; i++) {
		numNeurons += tp->neurons[i];
	}
	for (i = 0; i < numNeurons; i++) {
		free(tp->connections[i]);
	}
	free(tp->connections);
	free(tp->activationFunctions);
	free(tp->diffActivationFunctions);
	//free(tp->errorFunction);
	free(tp->neurons);
	free(tp->indexes);
}
